import { DisplayObject, Point } from "pixi.js";
import { Matrix, Transform } from "pixi.js";
import { BoneView } from "../views/BoneView";
import { Bone } from "../models/Bone";
import { ConnectionLine } from "../views/ConnectionLine";
import { DisplayController } from "./DisplayController";
import { ItransformableItem } from "./ITransformableItem";
import { Transform as MyTransform } from "../geom/Transform";

class BoneController implements ItransformableItem {
    private _view: BoneView;
    private _model: Bone;
    private _localMatrix: Matrix = Matrix.IDENTITY;
    private _children: BoneController[] = [];
    private _parent: BoneController | null = null;
    private _isSelected: boolean = false;
    private _level: number = 0;
    private _connectionLine: ConnectionLine | null = null;

    public displayList: DisplayController[] = [];
    public slotCount = 0;

    public static num = 0;
    public static genBoneName(): string {
        let name: string;
        if (this.num == 0) {
            name = "root";
        } else {
            name = `bone${this.num}`;
        }
        this.num++;
        return name;
    }

    constructor(view: BoneView, model: Bone) {
        this._view = view;
        this._model = model;

        model.name = BoneController.genBoneName();
    }

    public updateTail(x: number, y: number): void {
        this._view.updateTail(x, y);
        this._model.transform.rotation = this._view.rotation;
        this._model.length = this._view.arrowLength;
    }

    // public updateTailByPoint(pre: Point, cur: Point, scaleX1: number = 1, scaleY1: number = 1): void {
    //     let curBak = cur.clone();
    //     let { scaleX, scaleY } = this.getScale();

    //     let parent = this._parent;
    //     let position = this._view.position;
    //     if (parent) {
    //         pre = parent.view.toLocal(pre);
    //         cur = parent.view.toLocal(cur);
    //         position = parent.view.toLocal(position);
    //     }


    //     let rotationTo = Math.atan2((cur.y - position.y), (cur.x - position.x));
    //     let rotationFrom = Math.atan2((pre.y - position.y), (pre.x - position.x));
    //     let rotation = rotationTo - rotationFrom;


    //     this._model.transform.rotation += rotation;
    //     let scale = this.getPointOnEllipse(0, 0, Math.abs(scaleX), Math.abs(scaleY), this._model.transform.rotation, 0);
    //     this._view.updateTail(curBak.x, curBak.y, scale);
    //     console.log("scale scale", scale, scaleX, scaleY);
    //     this._model.length = this._view.arrowLength;


    //     this.updateMatrixByLocalTransform();
    //     this.updateSubItems();
    // }

    // private _click(): void {
    //     console.log("bone click");
    //     this._view.stage.on("pointermove", this._onBoneMove, this);
    //     console.log("localMatrix click", this._localMatrix);
    //     this._view.rotation += 0.02;
    //     this.updateLocalMatrix();
    //     this.updateSubItems();
    // }

    // private _onBoneMove(event: any): void {
    //     //this._view.position.set(event.global.x, event.global.y);
    //     console.log("bone move");
    //     this.updateLocalMatrix();
    //     this.updateSubItems();
    //     console.log("localMatrix move", this._localMatrix);
    // }

    // private _onBoneMoveEnd(): void {
    //     console.log("bone move end", this);
    //     // this.updateLocalMatrix();
    //     // this.updateSubItems();
    //     this._view.stage.off('pointermove', this._onBoneMove, this);

    // }

    public static normalizeRadian(value: number): number {
        value = (value + Math.PI) % (Math.PI * 2.0);
        value += value > 0.0 ? -Math.PI : Math.PI;

        return value;
    }

    public updateLocalMatrix(): void {
        this._view.updateTransform();
        if (this._parent == null) {
            this._view.localTransform.copyTo(this._localMatrix);
        } else {
            this._parent.view.updateTransform();
            let globalMatrix = this._view.localTransform;
            let parentGlobalMatrix = this._parent.view.localTransform;

            this._localMatrix.identity();
            this._localMatrix.append(parentGlobalMatrix.clone().invert()).append(globalMatrix);
        }

        this._model.transform.fromPixiMatrix(this._localMatrix);
        this._model.notifyUI();
    }

    public updateMatrixByLocalTransform(viewportScale: number = 1.0): void {
        const transform = this._model.transform;
        this._view.updateTransform();
        this._localMatrix.identity();
        this._localMatrix.scale(transform.scaleX, transform.scaleY);
        this._localMatrix.rotate(transform.rotation);
        this._localMatrix.translate(transform.x, transform.y);

        let parent = this._parent;
        if (parent) {
            let matrix = Matrix.IDENTITY;
            let boneTran = Transform.IDENTITY;

            matrix.copyFrom(parent.view.localTransform).append(this._localMatrix).decompose(boneTran);
        console.log("updateMatrixByLocalTransform pos", boneTran.position.x, boneTran.position.y);
            this._view.setTransform(boneTran.position.x, boneTran.position.y, boneTran.scale.x, boneTran.scale.y, boneTran.rotation, boneTran.skew.x, boneTran.skew.y);
        } else {
            let boneTran = Transform.IDENTITY;
            this._localMatrix.decompose(boneTran);
            this._view.setTransform(boneTran.position.x, boneTran.position.y, boneTran.scale.x, boneTran.scale.y, boneTran.rotation, boneTran.skew.x, boneTran.skew.y);
        }

        this._model.notifyUI();
    }

    public updateSelf(): void {
        if (this._parent !== null) {
            console.log("update self");
            let matrix = Matrix.IDENTITY;
            let boneTran = Transform.IDENTITY;
            matrix.copyFrom(this._parent.view.localTransform).append(this._localMatrix).decompose(boneTran);
            this._view.setTransform(this._view.position.x, this._view.position.y, boneTran.scale.x, boneTran.scale.y, boneTran.rotation, boneTran.skew.x, boneTran.skew.y);
        }
    }

    public updateSubItems(): void {
        console.log("update sub bone", this._level);
        this._view.updateTransform();
        this.updateConnectionLine();
        this.updateDisplay();

        for (let bone of this._children) {
            let matrix = Matrix.IDENTITY;
            let boneTran = Transform.IDENTITY;
            matrix.copyFrom(this._view.localTransform).append(bone.localMatrix).decompose(boneTran);
            bone.view.setTransform(boneTran.position.x, boneTran.position.y, boneTran.scale.x, boneTran.scale.y, boneTran.rotation, boneTran.skew.x, boneTran.skew.y);

            //update sub
            bone.updateSubItems();
            bone.updateConnectionLine();
        }
    }

    public updateDisplay(): void {
        this._view.updateTransform();
        for (let display of this.displayList) {
            if (display.view) {
                let matrix = Matrix.IDENTITY;
                let boneTran = Transform.IDENTITY;
                matrix.copyFrom(this._view.localTransform).append(display.localMatrix).decompose(boneTran);
                display.view.setTransform(boneTran.position.x, boneTran.position.y, boneTran.scale.x, boneTran.scale.y, boneTran.rotation, boneTran.skew.x, boneTran.skew.y);
                display.updateBorderView();
            }
        }
    }

    public selected(state: boolean) {
        this._isSelected = state;
        this._view.selected(state);
    }

    public createConnectionLine(beginPoint: Point, endPoint: Point): ConnectionLine {
        this._connectionLine = new ConnectionLine(beginPoint, endPoint);
        return this._connectionLine;
    }

    public updateConnectionLine() {
        let parent = this._parent;
        if (parent && this._connectionLine) {
            let beginPoint = (parent.view as BoneView).getTailPoint();
            let endPoint = this._view.position;
            this._connectionLine.updateBeginAndEndPoint(beginPoint, endPoint);
        }
    }

    public hideConnectionLine() {
        if (this._connectionLine) {
            this._connectionLine.visible = false;
        }
    }

    public showConnectionLine() {
        if (this._connectionLine) {
            this._connectionLine.visible = true;
        }
    }

    public hoverEnter() {
        console.log("hover enter bone", this.name);
        this._view.hoverEnter();
        this.showAllConnectionLine();
    }

    public hoverLeave() {
        if (this._isSelected) {
            this._view.selected(true);
        } else {
            this._view.hoverLeave();
        }
        this.hideAllConnectionLine();
    }

    public hideAllConnectionLine() {
        let subBones: BoneController[] = [];
        this._getSubBones(subBones);

        let parentsBones: BoneController[] = [];
        this._getParentsBones(parentsBones);

        let bones = parentsBones.concat(subBones, this);
        for (const bone of bones) {
            bone.hideConnectionLine();
        }
    }

    public showAllConnectionLine() {
        let subBones: BoneController[] = [];
        this._getSubBones(subBones);

        let parentsBones: BoneController[] = [];
        this._getParentsBones(parentsBones);

        let bones = parentsBones.concat(subBones, this);
        for (const bone of bones) {
            bone.showConnectionLine();
        }
    }

    private _getSubBones(result: BoneController[]): void {
        result.push(...this._children);
        for (let bone of this._children) {
            bone._getSubBones(result);
        }
    }

    private _getParentsBones(result: BoneController[]): void {
        let parent = this._parent;
        if (parent) {
            result.push(parent);
            parent._getParentsBones(result);
        }
    }

    public updateArrowLength(length: number) {
        this._view.updateArrowLength(length);
    }

    // public getPointOnEllipse(ptCenter_x: number, ptCenter_y: number, a: number, b: number, radian: number, dChangZhouAngle: number) {
    //     // ptCenter_x,y   :椭圆中心
    //     // a              :椭圆长轴长度
    //     // b              :椭圆短轴长度
    //     // radian         :点于椭圆长轴夹角的弧度
    //     // dChangZhouAngle:长轴弧度
    //     // let dLiXin = Math.atan2(a * Math.sin(radian), b * Math.cos(radian));  // 离心角
    //     // let x = a * Math.cos(dLiXin) * Math.cos(dChangZhouAngle) - b * Math.sin(dLiXin) * Math.sin(dChangZhouAngle) + ptCenter_x;
    //     // let y = a * Math.cos(dLiXin) * Math.sin(dChangZhouAngle) + b * Math.sin(dLiXin) * Math.cos(dChangZhouAngle) + ptCenter_y;
    //     // let dist = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));

    //     let dist = Math.sqrt(Math.pow(a, 2) * Math.pow(Math.cos(radian), 2) + Math.pow(b, 2) * Math.pow(Math.sin(radian), 2))

    //     return dist;
    // }

    public isContainPoint(point: Point): boolean {
        return this._view.isContainPoint(point);
    }

    get transform(): MyTransform {
        return this._model.transform;
    }

    get level(): number {
        return this._level;
    }

    set level(level: number) {
        this._level = level;
    }

    get name(): string {
        return this._model.name;
    }

    notifyUI(): void {
        this._model.notifyUI();
    }

    get parent(): BoneController | null {
        return this._parent;
    }

    set parent(bone: BoneController) {
        this._parent = bone;
    }

    get view(): DisplayObject {
        return this._view;
    }

    get children() {
        return this._children;
    }

    get model() {
        return this._model;
    }

    get localMatrix() {
        return this._localMatrix;
    }

}

export { BoneController };
